package com.food.eat.sso.controller;


import com.food.eat.pojo.Users;
import com.food.eat.pojo.vo.UserVo;
import com.food.eat.service.UserService;
import com.food.eat.util.FoodResultJson;
import com.food.eat.util.JsonUtils;
import com.food.eat.util.MD5Util;
import com.food.eat.util.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @Author:RenPu
 * @Date: 2020/8/5 12:57
 * @Version: 1.0
 * @description:
 */

@Controller
public class SSOController {


    //全局会话（分布式会话）
    public static final String REDIS_USER_TOKEN = "redis_user_token";

    //全局验证门票：验证用户是否登陆过CAS
    public static final String REDIS_USER_TICKET = "redis_user_ticket";

    //临时票据
    public static final String REDIS_TMP_TOKEN = "redis_tmp_token";

    //Cookie的ticket
    public static final String COOKIE_USER_TICKET = "cookie_user_ticket";


    @Resource
    private UserService userService;


    @Resource
    private RedisOperator redisOperator;


    @GetMapping("/SSOTest")
    @ResponseBody
    public String ssoTest() {

        return "hello----SSO";
    }


    /**
     * 用户已经登录过CAS系统，二次登录方法，验证全局票据的有效性，生成临时票据便于访问其他站点
     *
     * @param returnUrl
     * @param model
     * @param request
     * @param httpServletResponse
     * @return
     */
    @GetMapping("/login")
    @ResponseBody
    public String login(String returnUrl,
                        Model model,
                        HttpServletRequest request,
                        HttpServletResponse httpServletResponse) {
        model.addAttribute( "returnUrl", returnUrl );

        //TODO 后续完成用户是否登录校验

        //验证userticket，如果能cookie中取到cookie_user_ticket,则用户登陆过，直接生成一个临时票据，无须登录
        String userTicket = getCookie( request, COOKIE_USER_TICKET );

        //验证票据是否有效
        boolean verify = verifyUserTicket( userTicket );
        if (verify) {
            //创建临时票据
            String tmpTicket = createTmpTicket();
            return "redirect:" + returnUrl + "?tmpTicket" + tmpTicket;
        }

        return "login";
    }


    /**
     * CAS的登录验证方法，用户生成全局门票，Redis令牌，临时票据，以及将数据封装到Cookie中
     *
     * @param returnUrl
     * @param model
     * @param request
     * @param httpServletResponse
     * @param username
     * @param password
     * @return
     * @throws NoSuchAlgorithmException
     */
    @PostMapping("/doLogin")
    @ResponseBody
    public String doLogin(String returnUrl,
                          Model model,
                          HttpServletRequest request,
                          HttpServletResponse httpServletResponse,
                          String username, String password) throws NoSuchAlgorithmException {

        model.addAttribute("returnUrl", returnUrl);

        //判断是否为空
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            model.addAttribute("errmsg", "用户名称或者密码不允许为空");
            return "login";
        }
        Users users = userService.login(username, MD5Util.getMD5Str(password));

        if (users == null) {
            model.addAttribute("errmsg", "用户账号或者密码输入有误");
        }


        //TODO 实现注册时生成用户的redis会话
        String uniqueToken = UUID.randomUUID().toString().trim();

        UserVo userVo = new UserVo();
        BeanUtils.copyProperties(users, userVo);
        userVo.setUserUniqueToken(uniqueToken);

        //通过用户的ID值与会话进行绑定
        redisOperator.set(REDIS_USER_TOKEN + ":" + users.getId(), JsonUtils.objectToJson(userVo));

        //生成全局门票ticket，代表用户在此CAS端登陆过
        String userTicket = UUID.randomUUID().toString().trim();

        //USER_TICKET关联用户id,存储在redis中，代表此用户已经拥有门票，可以去各个小的景点观看 存储用户的ID值
        redisOperator.set(REDIS_USER_TICKET + ":" + userTicket, userVo.getId());

        //将生成的全局门票，放入CAS端Cookie便于从其他站点，其他网站便于校验
        setCookie(COOKIE_USER_TICKET, userTicket, httpServletResponse);

        //生成临时票据，回调到调用到网站，是由CAS端签发的一次性的ticket
        String tmpTicket = createTmpTicket();

        /**
         * userTicket:用于表示当前用户在CAS已经登陆过状态：已登录
         *
         * tmpTicket：用颁发给用户一次性的验证票据：具有时效性
         *
         */


        return "redirect:" + returnUrl + "?tmpTicket" + tmpTicket;
    }


    /**
     * 校验票据的有效性，并把根据用户的唯一ID值，获取会话的信息响应给前端进行展示
     *
     * @param tmpTicket
     * @param request
     * @param httpServletResponse
     * @return
     * @throws NoSuchAlgorithmException
     */
    @PostMapping("/verifyTmpTicket")
    @ResponseBody
    public FoodResultJson verifyTmpTicket(String tmpTicket,
                                          HttpServletRequest request,
                                          HttpServletResponse httpServletResponse) throws NoSuchAlgorithmException {
        String tmpTicketValue = redisOperator.get(REDIS_TMP_TOKEN + ":" + tmpTicket);
        if (StringUtils.isBlank(tmpTicketValue)) {
            return FoodResultJson.errMsg("用户票据异常！！！");
        }

        if (!tmpTicketValue.equals(MD5Util.getMD5Str(tmpTicket))) {
            return FoodResultJson.errMsg("用户票据异常！！！");
        } else {

            //销毁临时票据
            redisOperator.del(REDIS_TMP_TOKEN + ":" + tmpTicket);
        }

        //验证并且获取用户的userTicket

        //1:先从cookie获取，获取userTicket的值
        String userTicket = getCookie(request, COOKIE_USER_TICKET);

        //2：再从redis，根据userTicket作为用于区分的key来获取相对应存储的用户的ID值
        String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isBlank(userId)) {
            return FoodResultJson.errMsg("用户票据异常！！！");
        }

        //验证门票所对应的用户会话，是否在redis存在
        String userVo = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isBlank(userVo)) {
            return FoodResultJson.errMsg("用户票据异常！！！");

        }


        return FoodResultJson.ok(JsonUtils.jsonToPojo(userVo, UserVo.class));
    }


    /**
     * 退出方法，清除redis/cookie中的票据，以及全局会话的数据
     *
     * @param userId
     * @param request
     * @param httpServletResponse
     * @return
     */
    @GetMapping("/logout")
    @ResponseBody
    public FoodResultJson logout(String userId,
                                 HttpServletRequest request,
                                 HttpServletResponse httpServletResponse) {

        //先获取用户的ID值 获取CAS中用户的门票
        String userTicket = getCookie(request, COOKIE_USER_TICKET);

        //清除userTicket票据，redis以及cookie
        delCookie(COOKIE_USER_TICKET, httpServletResponse);
        redisOperator.del(REDIS_USER_TICKET + ":" + userTicket);


        //清除用户全局会话（分布式会话）
        redisOperator.del(REDIS_USER_TOKEN + ":" + userId);


        return FoodResultJson.ok();
    }

    Map map = new HashMap<>();


    /**
     * 封装获取cookie的方法
     *
     * @param httpServletRequest
     * @param key
     * @return
     */
    public String getCookie(HttpServletRequest httpServletRequest, String key) {

        Cookie[] cookies = httpServletRequest.getCookies();

        if (cookies == null && StringUtils.isBlank(key)) {
            return null;
        }

        String cookiesValues = null;
        for (int i = 0; i < cookies.length; i++) {
            if (cookies[i].getName().equals(key)) {
                cookiesValues = cookies[i].getValue();
                break;
            }
        }

        return cookiesValues;

    }


    /**
     * 生成临时签票，并储存在redis中，设置过期时间
     *
     * @return
     */
    public String createTmpTicket() {
        String tmpTicket = UUID.randomUUID().toString().trim();
        try {
            redisOperator.set(REDIS_TMP_TOKEN + ":" + tmpTicket, MD5Util.getMD5Str(tmpTicket), 6000);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        return tmpTicket;
    }


    /**
     * 封装数据到Cookie并创建Cookie对象
     *
     * @param key
     * @param value
     * @param httpServletResponse
     */
    public void setCookie(String key, String value, HttpServletResponse httpServletResponse) {
        Cookie cookie = new Cookie(key, value);
        cookie.setDomain("sso.com");
        cookie.setPath("/");
        httpServletResponse.addCookie(cookie);
    }


    /**
     * 移除Cookie
     *
     * @param key
     * @param httpServletResponse
     */
    public void delCookie(String key, HttpServletResponse httpServletResponse) {
        Cookie cookie = new Cookie(key, null);
        cookie.setDomain("sso.com");
        cookie.setPath("/");
        //设置为失效
        cookie.setMaxAge(-1);
        httpServletResponse.addCookie(cookie);
    }


    /**
     * 封装验证useTicket是否有效
     *
     * @param userTicket
     * @return
     */
    public boolean verifyUserTicket(String userTicket) {
        if (StringUtils.isBlank(userTicket)) {
            return false;
        }

        //2：再从redis，根据userTicket作为用于区分的key来获取相对应存储的用户的ID值
        String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isBlank(userId)) {
            return false;
        }

        //验证门票所对应的用户会话，是否在redis存在
        String userVo = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isBlank(userVo)) {
            return false;
        }

        return true;

    }


}
